home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 12 - 1996 / 12.02 Feb 96 / Adding Scripts to Menus / Scripts MenuCode / SCScriptsMenu.cp < prev    next >
Encoding:
Text File  |  1995-06-15  |  8.3 KB  |  325 lines  |  [TEXT/MMCC]

  1. // ===========================================================================
  2. // SCScriptsMenu.cp -- handle the scripts menu
  3. // ===========================================================================
  4. // © 1995 James Kaput, Jeremy Roschelle SimCalc Project
  5.  
  6. // This source code may be compiled into a commercial or non-commercial application
  7. // providing that the hmnu resource 140 is included and the following help balloon appears
  8. // when the user points at the script menu with Balloon help enabled:
  9.  
  10. //ScriptMenu
  11. //Copyright © 1995 
  12. //By Jeremy Roschelle
  13. //Use this menu to run scripts.
  14.  
  15. // Comment:
  16. // the scripts menu is actually just an attachment that can be added to the 
  17. // application. The attachment handles commands coming from the scripts menu
  18. // by dispatching to an appropriate scripts menu item
  19.  
  20. #include "SCScriptsMenu.h"
  21. #include "UScripting.h"
  22. #include "SCFinderUtility.h"
  23. #include "UEventUtils.h"
  24.  
  25. SCScriptsMenuHandler::SCScriptsMenuHandler(ResIDT inMenuID,
  26.                                     short inVRefNum, 
  27.                                     long inParID,
  28.                                      Int16 inMax) 
  29.     : LAttachment(msg_AnyMessage,true), mMenuID(inMenuID)
  30. {
  31.     // appends menu items for each script in the designated folder
  32.     if (inVRefNum != 0) {        
  33.         // set up data for iteration
  34.         Int16        count = 0;
  35.         Str255        scriptFileName;
  36.         HFileParam    fInfo;
  37.         fInfo.ioNamePtr = scriptFileName;
  38.         
  39.         // iterate through each item in the folder, inserting scripts
  40.         StFolderIterator    iter(inVRefNum, inParID);
  41.         while ((count++ <  inMax) && iter.Next(fInfo)) {
  42.             if (fInfo.ioFlFndrInfo.fdType == kOSAFileType) {
  43.                 FSSpec    spec;
  44.                 FSMakeFSSpec(inVRefNum, inParID, scriptFileName, &spec);
  45.                 AppendScript(spec);
  46.             }
  47.         }
  48.     }
  49. }
  50.  
  51.  
  52. SCScriptsMenuHandler::~SCScriptsMenuHandler()
  53. {
  54.     LListIterator    iter(mScripts,iterate_FromStart);
  55.     SCScriptsMenuItem    *item;
  56.     while (iter.Next(&item)) delete item;
  57. }
  58.  
  59. void 
  60. SCScriptsMenuHandler::PurgeScripts()
  61. {
  62.     LListIterator    iter(mScripts,iterate_FromStart);
  63.     SCScriptsMenuItem    *item;
  64.     while (iter.Next(&item)) item->PurgeScript();
  65. }
  66.  
  67. // we have to execute on 2 kinds of message, a status message and command message
  68. void    
  69. SCScriptsMenuHandler::ExecuteSelf(MessageT inMessage, void *ioParam)
  70. {
  71.     mExecuteHost = true;
  72.     // update status
  73.     if (inMessage == msg_CommandStatus) {
  74.         SCommandStatus    *status = (SCommandStatus *)ioParam;
  75.         if (HiWord(- status->command) == mMenuID) {
  76.             *status->enabled = true;
  77.             *status->usesMark = false;
  78.             mExecuteHost = false; // we handled it
  79.         }
  80.     }
  81.     // handle menu comand 
  82.     else if (HiWord(-inMessage) == mMenuID) {
  83.         Int16    index = LoWord(-inMessage);
  84.         if (index > mScripts.GetCount()) // attach a new script
  85.             AttachScriptDialog();
  86.         else {                             // execute an existing script
  87.             SCScriptsMenuItem    *item;
  88.             if (mScripts.FetchItemAt(index, &item)) {
  89.                 if (cmdKey & UEventUtils::GetModifiers())
  90.                     item->OpenScript();    // open on command key
  91.                 else item->RunScript();
  92.                 mExecuteHost = false; // we handled it
  93.             }
  94.         }
  95.     }
  96. }
  97.  
  98. void 
  99. SCScriptsMenuHandler::AttachScriptDialog()
  100. {
  101.     // get the file to attach
  102.     OSType                scriptFileType = kOSAFileType;
  103.     StandardFileReply    reply;
  104.     ::StandardGetFile(nil,1,&scriptFileType,&reply);
  105.         
  106.     if (reply.sfGood) AppendScript(reply.sfFile); 
  107. }
  108.  
  109. void
  110. SCScriptsMenuHandler::AppendScript(FSSpec &inScriptFile)
  111. {
  112.     MenuHandle menu = ::GetMenu(mMenuID);
  113.     if (! menu) return;
  114.     
  115.     SCScriptsMenuItem *item = new SCScriptsMenuItem(inScriptFile);
  116.                 
  117.     // insert into the menu
  118.     ::InsertMenuItem(menu,inScriptFile.name,mScripts.GetCount());
  119.     
  120.     // insert the corresponding class instance into the list
  121.     mScripts.InsertItemsAt(1,arrayIndex_Last,&item);
  122.                 
  123.     // insert balloon help into resource
  124.     AttachBalloonHelp(inScriptFile, mScripts.GetCount());
  125. }
  126.  
  127. void            
  128. SCScriptsMenuHandler::AttachBalloonHelp(FSSpec &inScriptFile,Int16 inIndex)
  129. {
  130.     Str255    text;
  131.     {    // get the text
  132.         Int16  fRefNum = ::FSpOpenResFile(&inScriptFile,fsRdPerm);
  133.         if (ResError()) return;
  134.         
  135.         // the first text resource has the description of the script (if any)
  136.         Handle outText = ::Get1IndResource('TEXT',1);
  137.          if  (outText) UFinder::Handle2PStr(outText,text);
  138.          else *text = 0;
  139.          
  140.          ::CloseResFile(fRefNum);
  141.      }
  142.      
  143.      {    // add the help
  144.          char        buffer[500];
  145.          MakeBalloonData(text,buffer);
  146.          InsertBalloonData(inIndex,buffer);
  147.      }
  148. }
  149.  
  150. void        
  151. SCScriptsMenuHandler::MakeBalloonData(Str255 inHelp, char *ioBuffer)
  152. {
  153.     Int16    mark = 2, // leave room to write number of bytes to end
  154.             data;
  155.     Int32    zeros = 0;
  156.     
  157.     if (*inHelp == 0) {       // no data
  158.         data = 0x0100; // skip this item
  159.         ::BlockMoveData(&data,&ioBuffer[mark],sizeof(Int16));
  160.         mark += sizeof(Int16);
  161.     }
  162.     else {
  163.         data = 0x0001; // direct string type
  164.         ::BlockMoveData(&data,&ioBuffer[mark],sizeof(Int16));
  165.         mark += sizeof(Int16);
  166.         
  167.         // write out the string
  168.         ::BlockMoveData(inHelp,&ioBuffer[mark],1 + *inHelp);
  169.         mark += 1 + *inHelp;
  170.         
  171.         // write out three zeros for the other strings
  172.         ::BlockMoveData(&zeros,&ioBuffer[mark],3);
  173.         mark += 3;
  174.     }
  175.     
  176.     // align buffer to an even word boundary
  177.     if (mark & 0x0001) ++mark;
  178.     
  179.     // add size to first word of buffer
  180.     ::BlockMoveData(&mark,ioBuffer,sizeof(mark));
  181. }
  182. void            
  183. SCScriptsMenuHandler::InsertBalloonData(Int16 inIndex, char *inBuffer)
  184. {
  185.     Handle    hmnu = ::Get1Resource('hmnu',mMenuID);
  186.     if (! hmnu) return;
  187.     
  188.     Int16    len = *(short *)inBuffer;
  189.     
  190.     // make some room in the handle
  191.     ::SetHandleSize(hmnu, ::GetHandleSize(hmnu) + len);
  192.     if (::MemError()) return;
  193.     
  194.     StHandleLocker    lock(hmnu);    // lock it down so we can safely dereference it
  195.     char        *help = *hmnu;
  196.     
  197.     // increment number of items
  198.     ++*(short  *)(help + 0x0A); // @ help + 0x0A
  199.     
  200.     // skip over existing items
  201.     {
  202.         Int16    itemsToSkip = inIndex + 2 - 1; // skip default and title resource, don't skip self
  203.         help +=  0x0C; // location of first msg record
  204.         do {
  205.             help += *(Int16 *)help;    // add the number of bytes to skip
  206.         } while (--itemsToSkip);
  207.     }
  208.     
  209.     // shift data out of the way
  210.     {
  211.         char     *dest, *end;
  212.         dest = help + len;
  213.         end = ((char *)*hmnu + ::GetHandleSize(hmnu));
  214.         ::BlockMoveData(help, dest,  end - dest);
  215.     }
  216.     // copy help data in
  217.     ::BlockMoveData(inBuffer, help, len);
  218.     
  219.     // we're done!
  220.     // we don't release the resource, because we don't want it to be purged
  221.     // we don't mark it as changed, because we don't want it written out
  222.     //::ChangedResource(hmnu);
  223. }
  224.  
  225. void
  226. SCScriptsMenuHandler::RemoveScript(Int16 inIndex)
  227. {
  228.     SCScriptsMenuItem *item;
  229.     MenuHandle menu = ::GetMenu(mMenuID);
  230.     if (! menu) return;
  231.     
  232.     if (mScripts.FetchItemAt(inIndex,&item) ) {
  233.         delete item;
  234.         mScripts.RemoveItemsAt(1,inIndex);
  235.         
  236.         ::DeleteMenuItem(menu,inIndex);
  237.     }
  238.     RemoveBalloonHelp(inIndex);
  239. }
  240.  
  241. void
  242. SCScriptsMenuHandler::RemoveBalloonHelp(Int16 inIndex)
  243. {
  244.     Handle    hmnu = ::Get1Resource('hmnu',mMenuID);
  245.     if (! hmnu) return;
  246.     
  247.     Int16    itemSize;
  248.     Int32    hSize = ::GetHandleSize(hmnu);
  249.         
  250.     {
  251.         StHandleLocker    lock(hmnu);    // lock it down so we can safely dereference it
  252.         char        *help = *hmnu;
  253.         
  254.         // decrement number of items
  255.         --*(short  *)(help + 0x0A); // @ help + 0x0A
  256.         
  257.         // skip over existing items
  258.         Int16    itemsToSkip = inIndex + 2 - 1; // skip default and title resource, don't skip self
  259.         help +=  0x0C; // location of first msg record
  260.         do {
  261.             help += *(Int16 *)help;    // add the number of bytes to skip
  262.         } while (--itemsToSkip);
  263.         
  264.         // shift data to close the gap
  265.         itemSize = *(short *)help; 
  266.         char     *end = (char *)*hmnu + hSize;
  267.         ::BlockMoveData(help + itemSize, help,  (end - help - itemSize));
  268.     }
  269.     
  270.     // make Handle shorter
  271.     ::SetHandleSize(hmnu,hSize - itemSize);
  272. }
  273.  
  274.  
  275.  
  276. SCScriptsMenuItem::~SCScriptsMenuItem()
  277. {
  278.     PurgeScript();
  279. }
  280.  
  281. void
  282. SCScriptsMenuItem::PurgeScript()
  283. {
  284.     // dispose the script id token
  285.     if (mScriptID != kOSANullScript && noErr == UScripting::DisposeScript(mScriptID))
  286.         mScriptID = kOSANullScript;
  287. }
  288.  
  289. OSErr
  290. SCScriptsMenuItem::OpenScript()
  291. {
  292.     PurgeScript(); // if the user is opening it, they probably will change it
  293.     return UFinder::SendFinderAEOpen(mFileSpec);
  294. }
  295.         
  296. OSErr    
  297. SCScriptsMenuItem::RunScript()
  298. {
  299.     OSErr    err = noErr;
  300.     // load the script if its not available yet
  301.     if (mScriptID == kOSANullScript) {
  302.         Handle            script = nil, text = nil;
  303.         short             fRefNum = -1;
  304.         
  305.         Try_ {
  306.             // open resource fork
  307.             fRefNum = ::FSpOpenResFile(&mFileSpec,fsRdPerm);
  308.             ThrowIfResError_();
  309.             // get the first script resource in the file
  310.             script = ::Get1IndResource('scpt',1); 
  311.             FailNIL_(script);
  312.             // Load it
  313.             UScripting::LoadScript(script,mScriptID);
  314.         }
  315.         Catch_(catchErr) {
  316.             err = catchErr;
  317.             SysBeep(0);
  318.         }
  319.         EndCatch_
  320.         if (fRefNum != -1) ::CloseResFile(fRefNum);
  321.     }
  322.     if (err == noErr) new URun1Script(mScriptID);    
  323.     return err;
  324. }
  325.